from CenterServer import server
from Libs.protobuf import CS_GS_pb2
from CenterServer.Models import cell
from CenterServer.Models import player
from Libs import user
import random


class Room():
    gateProto = None

    width = 100
    height = 100

    def __init__(self, ID):
        self.roomID = ID
        self.gateProto = server.GateInstance.gateProto
        self.mapArray = []
        self.members = {}

        self.needUpdateCells = {}

        for i in range(self.width):
            line = []
            for j in range(self.height):
                item = cell.Cell(i, j)
                if j == 0 or j == self.height - 1 or i == 0 or i == self.width - 1:
                    item.cellType = cell.CellType.Border
                line.append(item)
            self.mapArray.append(line)

    def addPlayers(self, players):
        for _, value in enumerate(players):
            userInfo = user.getUser(value)
            if userInfo is None:
                continue
            userInfo["roomid"] = self.roomID
            user.updateUser(userInfo, ["roomid"])
            self.members[value] = player.Player(value, userInfo["name"], self)
            self.members[value].cellTypeId = userInfo.get("celltypeid", 102)
            self.members[value].initManors()

            zipMsg = CS_GS_pb2.TransmitMsg()
            zipMsg.mail = value
            msgInfo = CS_GS_pb2.Map()
            self.mapToMsg(msgInfo)
            self.gateProto.transmitMessage(zipMsg, msgInfo)

            msgInfo = CS_GS_pb2.EnterRoom()
            self.playerToMsg(msgInfo.player, self.members[value])
            self.broadcastMsg(msgInfo)

    def mapToMsg(self, msgMap):
        msgMap.width = self.width
        msgMap.height = self.height

        for _, value in self.members.items():
            member = msgMap.players.add()
            self.playerToMsg(member, value)

    def playerToMsg(self, msg, member):
        msg.mail = member.mail
        msg.name = member.name
        msg.roomid = self.roomID
        msg.score = member.score
        msg.celltypeid = member.cellTypeId
        self.cellToMsg(msg.currentCell, member.currentCell)
        for value in member.allManorCells:
            item = msg.manors.add()
            self.cellToMsg(item, value)
        for value in member.allTrackCells:
            item = msg.tracks.add()
            self.cellToMsg(item, value)

    def cellToMsg(self, cellMsg, cellData):
        if cellData.cellType == cell.CellType.Null:
            cellMsg.celltype = CS_GS_pb2.CellNull
        elif cellData.cellType == cell.CellType.Border:
            cellMsg.celltype = CS_GS_pb2.CellBorder
        elif cellData.cellType == cell.CellType.Track:
            cellMsg.celltype = CS_GS_pb2.CellTrack
        elif cellData.cellType == cell.CellType.Manor:
            cellMsg.celltype = CS_GS_pb2.CellManor
        cellMsg.x = cellData.x
        cellMsg.y = cellData.y
        cellMsg.owner = cellData.owner
        cellMsg.tracker = cellData.tracker

    def deletePlayer(self, mail):
        if self.members.get(mail):
            member = self.members.pop(mail)
            for value in member.allManorCells:
                value.owner = ""
                value.cellType = cell.CellType.Null
            for value in member.allTrackCells:
                value.tracker = ""
                value.cellType = cell.CellType.Null
            print("delete player:", mail)

    def update(self, timeInterval):
        count = 0
        msgInfo = CS_GS_pb2.Transform()
        msgInfo.roomid = self.roomID
        for key, value in self.members.items():
            if value.active:
                count += 1
                value.update(timeInterval)
                posInfo = msgInfo.positions.add()
                posInfo.mail = key
                for pos in value.position:
                    posInfo.position.append(pos)
        if count > 0:
            self.broadcastMsg(msgInfo)

        count = 0
        msgInfo = CS_GS_pb2.UpdateCells()
        for _, value1 in self.needUpdateCells.items():
            for _, value2 in value1.items():
                item = msgInfo.cells.add()
                self.cellToMsg(item, value2)
                count += 1
        if count > 0:
            self.broadcastMsg(msgInfo)
            self.needUpdateCells.clear()

    def broadcastMsg(self, msgInfo):
        zipMsg = CS_GS_pb2.TransmitMsg()
        zipMsg.msgid = msgInfo.msgid
        for key, _ in self.members.items():
            zipMsg.mail = key
            self.gateProto.transmitMessage(zipMsg, msgInfo)

    def playerDirection(self, msg):
        member = self.members.get(msg.mail)
        member.direction = msg.direction

        if not member.active:
            member.active = True

        self.broadcastMsg(msg)

    def getBornCell(self):
        rects = self.getBlankLand()
        needs = self.getRect(rects, 3, 3)
        index = random.randint(0, len(needs) - 1)
        need = needs[index]
        x = random.randint(need[0], need[2])
        y = random.randint(need[1], need[3])
        return self.mapArray[x][y]

    def getBlankLand(self):
        result = []
        testMap = [[0]*self.width for i in range(self.height)]
        for i in range(1, self.width - 4):
            for j in range(1, self.height - 4):
                if testMap[i][j] == 0 and self.mapArray[i][j].cellType == cell.CellType.Null:
                    r = self.findRight(testMap, i, j, self.width - 4)
                    t = self.findTop(testMap, i, j, r, self.height - 4)
                    rect = (i, j, r, t, r - i + 1, t - j + 1)
                    self.fillTestMap(testMap, rect)
                    result.append(rect)
                    j = t
        return result

    def findRight(self, testMap, x, y, right):
        pos = x
        for i in range(x, right):
            pos = i
            if testMap[i + 1][y] == 1 or self.mapArray[i + 1][y].cellType != cell.CellType.Null:
                break
        return pos

    def findTop(self, testMap, x, y, right, top):
        pos = y
        for j in range(y, top):
            pos = j
            for i in range(x, right + 1):
                if testMap[i][j + 1] == 1 or self.mapArray[i][j + 1].cellType != cell.CellType.Null:
                    break
        return pos

    def fillTestMap(self, testMap, rect):
        for i in range(rect[0], rect[2] + 1):
            for j in range(rect[1], rect[3] + 1):
                testMap[i][j] = 1

    def getRect(self, rects, width, height):
        result = []
        for value in rects:
            if value[4] >= width and value[5] >= height:
                result.append(value)
        return result

    def setTrack(self, x, y, name, flag):
        targetCell = self.mapArray[x][y]
        targetCell.tracker = name
        targetCell.cellType = cell.CellType.Track
        targetCell.trackFlag = flag

        self.addUpdateCells(targetCell)

    def clearTrack(self, x, y):
        self.setTrack(x, y, "", cell.TrackFlag.Null)

    def setManor(self, x, y, name, needUpdate=True):
        targetCell = self.mapArray[x][y]
        oldName = targetCell.owner
        if self.members.get(oldName):
            oldOwner = self.members.get(oldName)
            if targetCell in oldOwner.allManorCells:
                oldOwner.allManorCells.remove(targetCell)

        newOwner = self.members.get(name)
        targetCell.owner = name
        targetCell.tracker = ""
        targetCell.cellType = cell.CellType.Manor
        newOwner.allManorCells.append(targetCell)

        if needUpdate:
            self.addUpdateCells(targetCell)

    def addUpdateCells(self, cellInfo):
        x, y = cellInfo.x, cellInfo.y
        if x not in self.needUpdateCells.keys():
            self.needUpdateCells[x] = {}
        self.needUpdateCells[x][y] = cellInfo
